#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import os
import shutil
import sys

execfile(os.path.join(sys.path[0], "framework.py"))

from oriolusdocker.docker import DockerFactory, DockerError
from oriolusdocker.storages.memorystorage import MemoryStorage
from oriolusgaffer.cache import Cache
from oriolusgaffer.gaffer.selectstrategy import LatestStrategy
from oriolusgaffer.gaffer.catalogue import Catalogue
from oriolusgaffer.gaffer import Gaffer, GafferError
from orioluspith.packets.meta import Meta
from orioluspith.packets.packet import Packet, Status
from pmock import *
from unittest import TestCase, TestLoader, TextTestRunner

class TestUpgrade(TestCase):
    def setUp(self):
        self.__test_packets_path = os.path.join(".", "testdata", "testpackets")

        self.__cache_path = os.path.join(".", "testdata", "gaffer_cache")
        self.__temp_dirs = (self.__cache_path, )

        self.__setup_mocks()

    def tearDown(self):
        for path in self.__temp_dirs:
            if os.path.exists(path):
                shutil.rmtree(path)
   
    def test_upgrade01(self):
        """
        Packet - packet_a-1.2.3.2 will be install.
                 packet_a-1.2.3.1 will be removed
        
        packet_a-1.2.3.1 is installed
        """
        
        packet_folder = "test_upgrade01"
        packet_name_a = "packet_a-1.2.3.2"                
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)

        trotter_packet_list = ["packet_a-1.2.3.2", ]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
        
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_a"), eq("1.2.3.2")).will(return_value(packet_path_a))

        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.1", None, [], [], [])))

        expected_docker_actions = ("search(get_all=eq(True)).will(return_value([Packet(Meta('packet_a', '1.2.3.1', None, [], [], []))]))",
                                   "search(full_name=eq('packet_a-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   
                                   "remove(eq('packet_a-1.2.3.1'))",
                                   "search(full_name=eq('packet_a-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   "install(packet_path=string_contains('packet_a-1.2.3.2'))", )

        self.assert_upgrade(expected_docker_actions)
        
    def test_upgrade02(self):
        """
        Packet - packet_a-1.2.3.3 will be installed.
                 packet_a-1.2.3.1 will be removed
                 packet_a-1.2.3.2 will be removed
                 
        Trotter list: packet_a-1.2.3.2, packet_a-1.2.3.3
        
        packet_a-1.2.3.1 is installed
        packet_a-1.2.3.2 is installed
        """
        
        packet_folder = "test_upgrade02"
        packet_name_a = "packet_a-1.2.3.3"                
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        
        trotter_packet_list = ["packet_a-1.2.3.2", "packet_a-1.2.3.3"]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
       
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_a"), eq("1.2.3.3")).will(return_value(packet_path_a))

        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.1", None, [], [], [])))
        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.2", None, [], [], [])))

        expected_docker_actions = ("search(get_all=eq(True)).will(return_value([Packet(Meta('packet_a', '1.2.3.1', None, [], [], [])), Packet(Meta('packet_a', '1.2.3.2', None, [], [], []))]))",
                                   
                                   "search(full_name=eq('packet_a-1.2.3.3'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                                                      
                                   "remove(eq('packet_a-1.2.3.1'))",
                                   "remove(eq('packet_a-1.2.3.2'))",                                   
                                   "search(full_name=eq('packet_a-1.2.3.3'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   "install(packet_path=string_contains('packet_a-1.2.3.3'))" )

        self.assert_upgrade(expected_docker_actions)
        
    def test_upgrade03(self):
        """
        Packet - packet_a-1.2.3.1 will be removed.
                 
        Trotter list: packet_a-1.2.3.2
        
        packet_a-1.2.3.1 is installed
        packet_a-1.2.3.2 is installed
        """
        
        packet_folder = "test_upgrade03"
        packet_name_a = "packet_a-1.2.3.2"                
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        
        trotter_packet_list = ["packet_a-1.2.3.2"]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
       
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_a"), eq("1.2.3.2")).will(return_value(packet_path_a))

        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.1", None, [], [], [])))
        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.2", None, [], [], [])))

        expected_docker_actions = ("search(get_all=eq(True)).will(return_value([Packet(Meta('packet_a', '1.2.3.1', None, [], [], [])), Packet(Meta('packet_a', '1.2.3.2', None, [], [], []))]))",
                                   "remove(eq('packet_a-1.2.3.1'))")

        self.assert_upgrade(expected_docker_actions)
        
    def test_upgrade04(self):
        """
        Packet - packet_a-1.2.3.6 will be installed.
                 packet_a-1.2.3.1 will be removed
                 packet_a-1.2.3.4 will be removed
                 
        Trotter list: packet_a-1.2.3.3, packet_a-1.2.3.5, packet_a-1.2.3.6
        
        packet_a-1.2.3.1 is installed
        packet_a-1.2.3.4 is installed
        """
        
        packet_folder = "test_upgrade04"
        packet_name_a = "packet_a-1.2.3.6"                
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        
        trotter_packet_list = ["packet_a-1.2.3.3", "packet_a-1.2.3.5", "packet_a-1.2.3.6"]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
       
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_a"), eq("1.2.3.6")).will(return_value(packet_path_a))

        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.1", None, [], [], [])))
        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.4", None, [], [], [])))

        expected_docker_actions = ("search(get_all=eq(True)).will(return_value([Packet(Meta('packet_a', '1.2.3.1', None, [], [], [])), Packet(Meta('packet_a', '1.2.3.4', None, [], [], []))]))",
                                   
                                   "search(full_name=eq('packet_a-1.2.3.6'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                                                      
                                   "remove(eq('packet_a-1.2.3.1'))",
                                   "remove(eq('packet_a-1.2.3.4'))",                                    
                                   "search(full_name=eq('packet_a-1.2.3.6'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   "install(packet_path=string_contains('packet_a-1.2.3.6'))")

        self.assert_upgrade(expected_docker_actions)
        
    def test_upgrade05(self):
        """
        Packet - packet_a-1.2.3.3 will be installed.
                 packet_a-1.2.3.1 will be removed
                 packet_a-1.2.3.2 will be removed
                 
        Trotter list: packet_a-1.2.3.3, packet_b-1.2.3.1
        
        packet_a-1.2.3.1 is installed
        packet_a-1.2.3.2 is installed
        """
        
        packet_folder = "test_upgrade05"
        packet_name_a = "packet_a-1.2.3.3"                
        packet_name_b = "packet_b-1.2.3.1"                
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        packet_path_b = self.__get_packet_path(packet_folder, packet_name_b)
        
        trotter_packet_list = ["packet_a-1.2.3.3", "packet_b-1.2.3.1"]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
       
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_a"), eq("1.2.3.3")).will(return_value(packet_path_a))
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_b"), eq("1.2.3.1")).will(return_value(packet_path_b))

        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.1", None, [], [], [])))
        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.2", None, [], [], [])))

        expected_docker_actions = ("search(get_all=eq(True)).will(return_value([Packet(Meta('packet_a', '1.2.3.1', None, [], [], [])), Packet(Meta('packet_a', '1.2.3.2', None, [], [], []))]))",
                                   
                                   "search(full_name=eq('packet_a-1.2.3.3'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   "remove(eq('packet_a-1.2.3.1'))",
                                   "remove(eq('packet_a-1.2.3.2'))",
                                   "search(full_name=eq('packet_a-1.2.3.3'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   "install(packet_path=string_contains('packet_a-1.2.3.3'))")
                                   
        self.assert_upgrade(expected_docker_actions)
        
    def test_upgrade06(self):
        """
        Packet - packet_a-1.2.3.3 will be installed.
                 packet_a-1.2.3.1 will be removed
                 packet_a-1.2.3.2 will be removed
                 
        Trotter list: packet_a-1.2.3.2, packet_a-1.2.3.3, packet_b-1.2.3.1
        
        packet_a-1.2.3.1 is installed
        packet_a-1.2.3.2 is installed
        """
        
        packet_folder = "test_upgrade06"
        packet_name_a = "packet_a-1.2.3.3"                
        packet_name_b = "packet_b-1.2.3.1"                
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        packet_path_b = self.__get_packet_path(packet_folder, packet_name_b)

        self.__cache.put_item(os.path.join(self.__test_packets_path, packet_folder, "packet_a-1.2.3.2.zip.ori"), "packet_a-1.2.3.2")
        
        trotter_packet_list = ["packet_a-1.2.3.2","packet_a-1.2.3.3", "packet_b-1.2.3.1"]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
        
        self.__mock_trotter.stubs().get_versions(eq("packet_a")).will(return_value(["1.2.3.2", "1.2.3.3"]))
                
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_a"), eq("1.2.3.3")).will(return_value(packet_path_a))
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_b"), eq("1.2.3.1")).will(return_value(packet_path_b))

        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.1", None, [], [], [])))
        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.2", None, [], [], [])))

        expected_docker_actions = ("search(get_all=eq(True)).will(return_value([Packet(Meta('packet_a', '1.2.3.1', None, [], [], [])), Packet(Meta('packet_a', '1.2.3.2', None, [], [], []))]))",
                                   
                                   "search(full_name=eq('packet_a-1.2.3.3'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   
                                   "remove(eq('packet_a-1.2.3.1'))",
                                   "remove(eq('packet_a-1.2.3.2'))",                                   
                                   "search(full_name=eq('packet_a-1.2.3.3'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   "install(packet_path=string_contains('packet_a-1.2.3.3'))")

        self.assert_upgrade(expected_docker_actions)
        
    def test_upgrade07(self):
        """
        Packet - No packet will be installed.
                 
        Trotter list: packet_a-1.2.3.1, packet_b-1.2.3.1, packet_c-1.2.3.1

        No packet is installed
        """
        
        trotter_packet_list = ["packet_a-1.2.3.1", "packet_b-1.2.3.1", "packet_c-1.2.3.1"]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
       
        expected_docker_actions = ("search(get_all=eq(True)).will(return_value([]))",)

        self.assert_upgrade(expected_docker_actions)
        
    def test_upgrade08(self):
        """
        Packet - No packet will be installed.
                 
        Trotter list: packet_a-1.2.3.3
        
        packet_a-1.2.3.3 is installed
        packet_c-1.2.3.1 is installed
        """
        
        packet_folder = "test_upgrade07"
        packet_name_a = "packet_a-1.2.3.3"                
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        
        trotter_packet_list = ["packet_a-1.2.3.3"]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
       
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_a"), eq("1.2.3.3")).will(return_value(packet_path_a))

        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.3", None, [], [], [])))
        self.__memory_storage.add(Packet(Meta("packet_c", "1.2.3.1", None, [], [], [{"Name": "packet_a", "Version": "1.2.3.1"}])))

        expected_docker_actions = ("search(get_all=eq(True)).will(return_value([Packet(Meta('packet_a', '1.2.3.1', None, [], [], [])), Packet(Meta('packet_c', '1.2.3.1', None, [], [], [{'Name': 'packet_a', 'Version': '1.2.3.1'}]))]))",
                                   
                                   "search(full_name=eq('packet_a-1.2.3.3'), status=eq(Status.INSTALLED)).will(return_value([]))")

        mock_docker = self.__mock_docker

        for i in range(len(expected_docker_actions)):
            action = expected_docker_actions[i]

            if i == 0:
                eval("mock_docker.expects(once()).%s.id(i)" % action)
            else:
                eval("mock_docker.expects(once()).%s.id(i).after(i-1)" % action)

        self.assertRaises(GafferError, self.__gaffer.upgrade)        
        self.__mock_docker.verify()
        
    def test_upgrade09(self):
        """
        Packet - packet_a-1.2.3.1 will be removed
                 packet_b-1.2.3.1 will be removed
                 packet_a-1.2.3.2 will be installed
                 packet_b-1.2.3.2 will be installed
                 
        Trotter list: packet_a-1.2.3.2, packet_b-1.2.3.2
        
        packet_b-1.2.3.2 depends on packet_a-1.2.3.2
        
        packet_a-1.2.3.1 is installed
        packet_b-1.2.3.1 is installed
        """
        
        packet_folder = "test_upgrade08"
        packet_name_a = "packet_a-1.2.3.2"                
        packet_name_b = "packet_b-1.2.3.2"                
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        packet_path_b = self.__get_packet_path(packet_folder, packet_name_b)
        
        trotter_packet_list = ["packet_a-1.2.3.2", "packet_b-1.2.3.2"]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
        
        self.__mock_trotter.stubs().get_versions(eq("packet_a")).will(return_value(["1.2.3.2"]))
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_a"), eq("1.2.3.2")).will(return_value(packet_path_a))
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_b"), eq("1.2.3.2")).will(return_value(packet_path_b))

        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.1", None, [], [], [])))
        self.__memory_storage.add(Packet(Meta("packet_b", "1.2.3.1", None, [], [], [{"Name": "packet_a", "Version": "1.2.3.1"}])))

        expected_docker_actions = ("search(get_all=eq(True)).will(return_value([Packet(Meta('packet_a', '1.2.3.1', None, [], [], [])), Packet(Meta('packet_b', '1.2.3.1', None, [], [], [{'Name': 'packet_a', 'Version': '1.2.3.1'}]))]))",                                  
                                                                      
                                   "search(full_name=eq('packet_a-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   "search(full_name=eq('packet_b-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   "search(full_name=eq('packet_a-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                                                      
                                   "remove(eq('packet_b-1.2.3.1'))", 
                                   "remove(eq('packet_a-1.2.3.1'))",                                   
                                   "search(full_name=eq('packet_a-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([]))",                                   
                                   "install(packet_path=string_contains('packet_a-1.2.3.2'))",                                                                      
                                   "search(full_name=eq('packet_b-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   "search(full_name=eq('packet_a-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([object()]))",
                                   "install(packet_path=string_contains('packet_b-1.2.3.2'))")

        self.assert_upgrade(expected_docker_actions)
        
    def test_upgrade10(self):
        """
        Packet - packet_a-1.2.3.1 will be removed
                 packet_a-1.2.3.2 will be installed
                 
        Trotter list: packet_a-1.2.3.2, packet_b-1.2.3.1
        
        packet_b-1.2.3.1 depends on packet_a-[1.2.3.1, 1.2.3.2]
        
        packet_a-1.2.3.1 is installed
        packet_b-1.2.3.1 is installed
        """

        packet_folder = "test_upgrade09"
        packet_name_a = "packet_a-1.2.3.2"                
        packet_name_b = "packet_b-1.2.3.1"                
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        packet_path_b = self.__get_packet_path(packet_folder, packet_name_b)
        
        trotter_packet_list = ["packet_a-1.2.3.2", "packet_b-1.2.3.1"]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
       
        self.__mock_trotter.stubs().get_versions(eq("packet_a")).will(return_value(["1.2.3.2"]))
        
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_a"), eq("1.2.3.2")).will(return_value(packet_path_a))
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_b"), eq("1.2.3.1")).will(return_value(packet_path_b))

        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.1", None, [], [], [])))
        self.__memory_storage.add(Packet(Meta("packet_b", "1.2.3.1", None, [], [], [{"Name": "packet_a", "Version": "[1.2.3.1,1.2.3.2]"}])))

        expected_docker_actions = ("search(get_all=eq(True)).will(return_value([Packet(Meta('packet_a', '1.2.3.1', None, [], [], [])), Packet(Meta('packet_b', '1.2.3.1', None, [], [], [{'Name': 'packet_a', 'Version': '[1.2.3.1, 1.2.3.2]'}]))]))",                                  
                                                                      
                                   "search(full_name=eq('packet_a-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   
                                   "remove(eq('packet_b-1.2.3.1'))", 
                                   "remove(eq('packet_a-1.2.3.1'))",                                   
                                   "search(full_name=eq('packet_a-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([]))",                                   
                                   "install(packet_path=string_contains('packet_a-1.2.3.2'))",                                                                      
                                   "search(full_name=eq('packet_b-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([]))",
                                   "search(full_name=eq('packet_a-1.2.3.2'), status=eq(Status.INSTALLED)).will(return_value([object()]))",
                                   "install(packet_path=string_contains('packet_b-1.2.3.1'))")

        self.assert_upgrade(expected_docker_actions)
    
    def __setup_mocks(self):
        self.__cache = Cache(self.__cache_path)
        self.__mock_docker = Mock()
        
        self.__mock_trotter = Mock()
        catalogue = Catalogue(self.__mock_trotter)
        
        select_strategy = LatestStrategy()
        self.__gaffer = Gaffer(self.__cache, self.__mock_docker, self.__mock_trotter, catalogue, select_strategy)

        self.__memory_storage = MemoryStorage()
        self.__mock_docker.stubs().extract_storage().will(return_value(self.__memory_storage))

        self.__mock_virtual_docker = None
        self.__original_mock_virtual_docker_install = None
        self.__exceptional_packet_path = None

    def assert_upgrade(self, expected_docker_actions):
        mock_docker = self.__mock_docker

        for i in range(len(expected_docker_actions)):
            action = expected_docker_actions[i]

            if i == 0:
                eval("mock_docker.expects(once()).%s.id(i)" % action)
            else:
                eval("mock_docker.expects(once()).%s.id(i).after(i-1)" % action)

        self.__gaffer.upgrade()
        self.__mock_docker.verify()

    def __get_packet_path(self, packet_folder, packet_name):
        return os.path.join(self.__test_packets_path, packet_folder, packet_name + ".zip.ori") 

if __name__ == "__main__":
    suite = TestLoader().loadTestsFromTestCase(TestUpgrade)
    TextTestRunner(verbosity=2).run(suite)